package com.myaihui.service.impl;

import com.myaihui.domain.Resource;
import com.myaihui.domain.Role;
import com.myaihui.domain.rel.RelRoleResource;
import com.myaihui.domain.rel.RelUserRole;
import com.myaihui.foundation.model.PagerQuery;
import com.myaihui.foundation.query.RoleQuery;
import com.myaihui.repository.RelRoleResourceRepository;
import com.myaihui.repository.ResourceRepository;
import com.myaihui.repository.RoleRepository;
import com.myaihui.repository.UserRoleRepository;
import com.myaihui.service.RoleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by sun on 2018/7/3.
 *
 * @author sunfuchang03@126.com
 * @version 1.0
 * @since 1.0
 */
@Service
public class RoleServiceImpl extends BasePageableServiceImpl<Role, String> implements RoleService {
    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *
     * Static fields/constants/initializer
     *
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    private static Logger log = LoggerFactory.getLogger(RoleServiceImpl.class);

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *
     * Instance fields
     *
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    private RoleRepository roleRepository;
    @Autowired
    private ResourceRepository resourceRepository;
    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private RelRoleResourceRepository roleResourceRepository;

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *
     * Constructors
     *
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    public RoleServiceImpl(RoleRepository roleRepository) {
        super(roleRepository);
        this.roleRepository = roleRepository;
    }

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *
     * Public Methods
     *
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    @Override
    public Specification<Role> buildSpecification(PagerQuery pagerQuery) {
        Assert.notNull(pagerQuery, "Query must not be null.");
        RoleQuery query = (RoleQuery) pagerQuery;
        return (root, cq, cb) -> {
            String name = query.getName();
            List<Predicate> predicates = new LinkedList<>();
            if (StringUtils.hasLength(name)) {
                predicates.add(cb.like(root.get("name"), "%" + name + "%"));
            }
            Integer type = query.getType();
            if (type != null) {
                predicates.add(cb.equal(root.get("type"), type));
            } else {
                // -1是超级管理员
                predicates.add(cb.notEqual(root.get("type"), -1));
            }
            if (predicates.size() > 0) {
                cq.where(predicates.toArray(new Predicate[predicates.size()]));
            }
            return cq.getRestriction();
        };
    }

    @Override
    public Role getByName(String name) {
        Assert.hasLength(name, "Name must not be empty.");
        List<Role> roles = roleRepository.findByNameIs(name);
        return roles == null || roles.size() == 0 ? null : roles.get(0);
    }

    @Override
    public List<Resource> findResourcesByRoleId(String roleId) {
        Assert.hasLength(roleId, "RoleId must not be null.");
        return resourceRepository.findByRoleId(roleId);
    }

    @Override
    public List<Role> findByType(Integer type) {
        if (type == null) {
            return new ArrayList<>();
        }
        return roleRepository.findByType(type);
    }

    @Override
    @Transactional
    public void saveUserRoles(String userId, List<String> roleIds) {
        Assert.hasLength(userId, "UserId must not be empty");
        Assert.notNull(roleIds, "RoleIds must not be null");
        Assert.notEmpty(roleIds, "RoleIds must not be empty");
        userRoleRepository.deleteByUserId(userId);
        for (String roleId : roleIds) {
            userRoleRepository.save(RelUserRole.of(userId, roleId));
        }
    }

    @Override
    public List<Role> findByUserId(String userId) {
        Assert.hasLength(userId, "userid must not be null");
        return roleRepository.findByUserId(userId);
    }

    @Override
    @Transactional
    public void saveRoleWithResources(Role role, String[] resourceIds) {
        Assert.notNull(role, "Role must not be null");
        String id = role.getId();
        if (StringUtils.hasLength(id)) {
            Role src = roleRepository.findOne(id);
            src.setName(role.getName());
            src.setType(role.getType());
            src.setDescription(role.getDescription());
            roleRepository.save(src);
            roleResourceRepository.deleteByRoleId(id);
        } else {
            role.setCreated(new Date());
            role.setAvailable(true);
            id = roleRepository.save(role).getId();
        }
        Assert.hasLength(id, "Role id is required, but found null, there may be have some exception.");
        if (resourceIds != null && resourceIds.length > 0) {
            for (String resourceId : resourceIds) {
                RelRoleResource roleResource = RelRoleResource.of(id, resourceId);
                roleResourceRepository.save(roleResource);
            }
        }
    }

    @Override
    @Transactional
    public void deleteWithResources(String roleId) {
        Assert.hasLength(roleId, "roleId must not be null");
        roleResourceRepository.deleteByRoleId(roleId);
        roleRepository.delete(roleId);
    }

    @Override
    public Role getByNameExcludeId(String name, String roleId) {
        Assert.hasLength(name, "name must not be null");
        Assert.hasLength(roleId, "roleId must not be null");
        return roleRepository.findByNameAndIdIsNot(name, roleId);
    }

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *
     * Private Methods
     *
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */


}